﻿Public Class CDataDiff_PkInt
    'Inputs
    Public SourceDb As CDataSrc
    Public TargetDb As CDataSrc
    Public TableName As String
    Public PrimaryKey As String

    'Data
    Public SourceTable As DataTable
    Public TargetTable As DataTable

    'PK-Index/Diff
    Public SourcePkIndex As Dictionary(Of Integer, DataRow)
    Public TargetPkIndex As Dictionary(Of Integer, DataRow)
    Public DiffOnPk As CDiff_Int

    'Data-Hashing Index/Diff
    Public SourceRowHashIndex As Dictionary(Of Guid, List(Of Integer))
    Public TargetRowHashIndex As Dictionary(Of Guid, List(Of Integer))
    Public DiffOnRowHash As CDiff_Guid

    'Exact match rows
    Public ExactMatchPks As List(Of Integer)

    'Used for Resolve Method
    Public SourcePk2HashIndex As Dictionary(Of Integer, Guid)
    Public TargetPk2HashIndex As Dictionary(Of Integer, Guid)


    'Constructor
    Public Sub New(source As CDataSrc, target As CDataSrc, tableName As String)
		If Not tableName.Contains("[") Then tableName = String.Concat("[", tableName.Replace(".", "].["), "]")

		Me.SourceDb = source
		Me.TargetDb = target
		Me.TableName = tableName

		'Examine
		Me.SourceTable = source?.SelectAll_Dataset(tableName)?.Tables(0)
		Me.TargetTable = target?.SelectAll_Dataset(tableName)?.Tables(0)

		'Index/Diff By PK
		Me.PrimaryKey = SourceTable?.Columns(0)?.ColumnName
		Me.SourcePkIndex = CDiffLogic.TableIndex_PkInt(Me.SourceTable)
        Me.TargetPkIndex = CDiffLogic.TableIndex_PkInt(Me.TargetTable)
        Me.DiffOnPk = CDiffLogic.Diff_PKInt(Me.SourcePkIndex, Me.TargetPkIndex)

        'Index/Diff By Md5
        Me.SourceRowHashIndex = CDiffLogic.RowHashing_PkInt(SourceTable)
        Me.TargetRowHashIndex = CDiffLogic.RowHashing_PkInt(TargetTable, SourceTable)
        Me.DiffOnRowHash = CDiffLogic.Diff_MD5(Me.SourceRowHashIndex, Me.TargetRowHashIndex)

        'Union of pk/md5 matching
        Me.ExactMatchPks = CDiffLogic.Diff_Exact(Me)

        'Used for Resolve Method
        Me.SourcePk2HashIndex = CDiffLogic.ReverseHash_PKInt(SourceRowHashIndex)
        Me.TargetPk2HashIndex = CDiffLogic.ReverseHash_PKInt(TargetRowHashIndex)
    End Sub

	Public ReadOnly Property SourceOnlyRows_ByMD5 As DataTable
		Get
			Return GuidsToDataTable(SourceTable, DiffOnRowHash.SourceOnly, SourceRowHashIndex, SourcePkIndex)
		End Get
	End Property
	Public ReadOnly Property TargetOnlyRows_ByMD5 As DataTable
		Get
			Return GuidsToDataTable(TargetTable, DiffOnRowHash.TargetOnly, TargetRowHashIndex, TargetPkIndex)
		End Get
	End Property
	Public ReadOnly Property MatchingRows_ByMD5 As DataTable
		Get
			Return GuidsToDataTable(TargetTable, DiffOnRowHash.Matching, TargetRowHashIndex, TargetPkIndex)
		End Get
	End Property
	Public ReadOnly Property SourceOnlyRows_ByPk As DataTable
		Get
			Return PKsToDataTable(SourceTable, DiffOnPk.SourceOnly, SourcePkIndex)
		End Get
	End Property
	Public ReadOnly Property TargetOnlyRows_ByPk As DataTable
		Get
			Return PKsToDataTable(TargetTable, DiffOnPk.TargetOnly, TargetPkIndex)
		End Get
	End Property
	Public ReadOnly Property MatchingRows_ByPk As DataTable
		Get
			Return PKsToDataTable(TargetTable, DiffOnPk.Matching, TargetPkIndex)
		End Get
	End Property


	Private Shared Function PKsToDataTable(original As DataTable, pks As List(Of Integer), pkIndex As Dictionary(Of Integer, DataRow)) As DataTable
		Dim dt As New DataTable()
		For Each i As DataColumn In original.Columns
			dt.Columns.Add(i.ColumnName, i.DataType)
		Next

		For Each i As DataRow In PKsToRows(pks, pkIndex)
			dt.ImportRow(i)
		Next
		Return dt
	End Function
	Private Shared Function PKsToRows(pks As List(Of Integer), pkIndex As Dictionary(Of Integer, DataRow)) As List(Of DataRow)

		Dim list As New List(Of DataRow)
		For Each i As Integer In pks
			list.Add(pkIndex(i))
		Next
		Return list
	End Function

	Private Shared Function GuidsToDataTable(original As DataTable, guids As List(Of Guid), hashIndex As Dictionary(Of Guid, List(Of Integer)), pkIndex As Dictionary(Of Integer, DataRow)) As DataTable
		Dim dt As New DataTable()
		For Each i As DataColumn In original.Columns
			dt.Columns.Add(i.ColumnName, i.DataType)
		Next

		For Each i As DataRow In GuidsToRows(guids, hashIndex, pkIndex)
			dt.ImportRow(i)
		Next
		Return dt
    End Function
    Private Shared Function GuidsToRows(guids As List(Of Guid), hashIndex As Dictionary(Of Guid, List(Of Integer)), pkIndex As Dictionary(Of Integer, DataRow)) As List(Of DataRow)

        Dim list As New List(Of DataRow)
        For Each i As Guid In guids
            Dim pks As List(Of Integer) = hashIndex(i)
			For Each j As Integer In pks
				list.Add(pkIndex(j))
			Next
		Next
        Return list
    End Function

	'Resolver (fix data)
	Public Sub Resolve(isIdentity As Boolean)
		'Delete ids not in source
		For Each i As Integer In DiffOnPk.TargetOnly
			TargetDb.Delete(New CWhere(TableName, New CCriteria(PrimaryKey, i), Nothing))
		Next

		'insert new ids
		Dim colS As DataColumnCollection = SourceTable.Columns
		Dim cn As IDbConnection = TargetDb.Local.Connection
		Dim tx As IDbTransaction = cn.BeginTransaction()
		Try
			Dim testTbl As DataTable = TargetDb.SelectAll_Dataset("TOP 0 *", TableName, PrimaryKey).Tables(0)
			Dim colNames As New List(Of String)(testTbl.Columns.Count)
			For Each col As DataColumn In testTbl.Columns
				colNames.Add(col.ColumnName)
			Next

			If isIdentity Then TargetDb.SetIdentityInsert(True, TableName, tx)
			For Each i As Integer In DiffOnPk.SourceOnly
				Dim dr As DataRow = SourcePkIndex(i)
				Dim nv As New CNameValueList(colS.Count - 1)
				For Each j As DataColumn In colS
					If j.ColumnName = "__hash__" Then Continue For
					If j.ColumnName = "TimeStamp" Then Continue For
					If dr.IsNull(j) Then Continue For
					If Not colNames.Contains(j.ColumnName) Then Continue For
					nv.Add(j.ColumnName, dr(j))
				Next

				Dim id As Object = TargetDb.Insert(TableName, PrimaryKey, True, nv, tx, Nothing)
			Next
			If isIdentity Then TargetDb.SetIdentityInsert(True, TableName, tx)

			tx.Commit()
		Catch ex As Exception
			tx.Rollback()
			Throw ex
		Finally
			cn.Close()
		End Try


		'update common ids
		For Each i As Integer In DiffOnPk.Matching
			Dim s As Guid = SourcePk2HashIndex(i)
			Dim t As Guid = TargetPk2HashIndex(i)
			If s = t Then Continue For

			Dim dr As DataRow = SourcePkIndex(i)
			Dim nv As New CNameValueList(colS.Count - 1)
			For Each j As DataColumn In colS
				If j.ColumnName = PrimaryKey Then Continue For
				If j.ColumnName = "__hash__" Then Continue For
				If j.ColumnName = "TimeStamp" Then Continue For
				If dr.IsNull(j) Then Continue For
				nv.Add(j.ColumnName, dr(j))
			Next

			TargetDb.Update(nv, New CWhere(TableName, New CCriteria(PrimaryKey, i), Nothing))
		Next

		CCache.ClearAll()
	End Sub
	Public Sub ResolveByHash()
		'Delete hashes not in source
		For Each i As Guid In DiffOnRowHash.TargetOnly
			For Each pk As Integer In Me.TargetRowHashIndex(i)
				Dim sourceRow As DataRow = Me.SourcePkIndex(pk)
				If Not Me.TargetPkIndex.ContainsKey(pk) Then
					TargetDb.Delete(New CWhere(TableName, New CCriteria(PrimaryKey, pk), Nothing))
				Else
					Dim nv As New CNameValueList
					For Each col As DataColumn In SourceTable.Columns
						If col.ColumnName = "__hash__" Then Continue For
						If col.ColumnName = "TimeStamp" Then Continue For
						If col.ColumnName = PrimaryKey Then Continue For

						Dim obj As Object = sourceRow(col)
						If IsNothing(obj) Then obj = DBNull.Value
						nv.Add(col.ColumnName, obj)
					Next
					TargetDb.Update(nv, New CWhere(TableName, New CCriteria(PrimaryKey, pk), Nothing))
				End If
			Next

		Next

		'insert  hashes only in source
		Dim colS As DataColumnCollection = SourceTable.Columns
        For Each i As Guid In DiffOnRowHash.SourceOnly
            For Each j As Integer In SourceRowHashIndex(i)
                Dim dr As DataRow = SourcePkIndex(j)

                Dim nv As New CNameValueList(colS.Count - 1)
				For Each k As DataColumn In colS
					If k.ColumnName = "__hash__" Then Continue For
					If k.ColumnName = "TimeStamp" Then Continue For
					If k.ColumnName = PrimaryKey Then Continue For
					nv.Add(k.ColumnName, dr(k))
                Next

				Dim drx As DataRow = Nothing
				If TargetRowHashIndex.ContainsKey(i) Then
					Dim pk As Integer = TargetRowHashIndex(i)(0)
					TargetDb.Update(nv, New CWhere(TableName, New CCriteria(PrimaryKey, pk), Nothing))
					TargetRowHashIndex(i).RemoveAt(0)
				Else
					TargetDb.Insert(TableName, PrimaryKey, True, nv, Nothing, Nothing)
				End If

			Next
        Next


        CCache.ClearAll()
    End Sub
End Class
